import Cocoa

//var str = "Hello, playground"


//结构体的使用:
//1.计算属性:使用setter和getter方法
struct Point {
    var x = 0.0
    var y = 0.0
}

struct Size {
    var width = 0
    var height = 0
}

struct Rect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            return Point (x: origin.x + Double((size.width / 2)), y: origin.y + Double(size.height / 2))
        }
        set (newCenter) {
            origin.x = newCenter.x - Double(size.width / 2)
            origin.y = newCenter.y - Double(size.height / 2)
        }
    }
}

/*
 * 简写setter和getter方法:
 * 1.简写getter方法可以写成单一表达式的形式:去调用return关键字(XCode10版本是不能去掉return的,但是xcode11是可以的)
 * 2.简写setter方法就是可以去掉参数,新的参数系统可以用newValue来代替
 */
struct Rect2 {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            return Point(x: origin.x + Double(size.width / 2), y: origin.y + Double(size.height / 2))
        }
        set {
            origin.x = newValue.x - Double(size.width / 2)
            origin.y = newValue.y - Double(size.height / 2)
        }
    }
}


/*
 * 属性观察者:willSet,didSet
 * willSet:会在该值被存储之前调用,如果没有命名,默认使用newValue
 * didSet:会在一个新值被存储之后被调用,可以使用默认的形参名:oldValue
 */

//为类实现属性观察者
class StepCounter {
    var totalSteps: Int = 0 {
        willSet(newTotalSteps) {
            print("about set to totalSteps to \(newTotalSteps)")
        }
        didSet {
            if totalSteps > oldValue {
                print("Add \(totalSteps - oldValue) steps")
            }
        }
    }
}
let stepCounters = StepCounter()
stepCounters.totalSteps = 200
stepCounters.totalSteps = 360
stepCounters.totalSteps = 980


/*
 * 如何为结构体,类,枚举添加方法
 */

/**
 实例方法是属于特定类实例,结构体实例或者枚举实例的函数.他们为这些类提供功能性,要么通过提供访问和修改实例属性的方法,要么通过提供与实例目的相关的功能
 
 实例方法 - self
 1. 每一个类的实例都隐含一个叫做self的属性,它完完全全与实例本身相等.你可以使用self属性来在当前实例当中调用他自身的方法.
 2. 实际上,你不需要经常在代码中写self.如果你没有显示的写出self,Swift会在你于方法中使用已知属性或方法的时候假定你是调用了当前实例的属性或方法.
 3. 例外就是当一个实例方法的形式参数名与实例中某个属性拥有相同名字的时候.在这种情况下,形式参数具有优先权.可以使用self属性来区分形式参数名和属性名
*/
struct Point2 {
    var x = 0.0
    var y = 0.0
    func isToTheRightOf(x: Double) -> Bool {
        return self.x > x   //因为实例方法的形式参数名和属性名一致,所以此时需要显示的使用self来区分形式参数名和属性名
    }
}
let somePoint = Point2(x: 4.0, y: 5.0)
if somePoint.isToTheRightOf(x: 1.0){
    print("这个点往右移了")
}

//因为结构体和枚举都是值类型.默认情况下,值类型的属性不能被自身的实例方法修改,可以在func前面添加mutating关键字来指定才能被修改
struct Point3 {
    var x = 0.0, y = 0.0
    mutating func moveBy(x: Double, y: Double) {
        self.x += x
        self.y += y
    }
}
var somePoint2 = Point3(x: 1.0, y: 1.0)
somePoint2.moveBy(x: 2.0, y: 3.0)
print("the point is at \(somePoint2.x),\(somePoint2.y)")


//在mutating方法中赋值给self,mutating方法可以指定整个实例给隐含的self属性
struct Point4 {
    var x = 0.0, y = 0.0
    mutating func moveBy2(x: Double, y: Double) {
        self = Point4 (x: self.x + x, y: self.y + y)
    }
}
var somePoint3 = Point4(x: 1.0, y: 1.0)
somePoint3.moveBy2(x: 1.0, y: 3.0)
print("the point is at \(somePoint3.x),\(somePoint3.y)")

//枚举的异变方法(mutating)可以设置隐含的self属性为相同枚举里的不同成员
enum TriStateSwitch {
    case off, low, high
    mutating func next() {
        switch self {
        case .off:
            self = .low
        case .low:
            self = .high
        case .high:
            self = .off
        }
    }
}
var ovenLight = TriStateSwitch.low
ovenLight.next()   //打印: high
ovenLight.next()   //打印: off



/**
 为类,结构体,枚举添加下标操作
 1. 类,结构体,枚举可以定义下标,他可以作为访问集合,集合或序列成员元素的快捷方式.你可以使用下标通过索引值来设置或检索值而不需要为设置和检索分别使用实例方法.
 2. 你可以为一个类型定义多个下标,并且下标会基于传入的索引值的类型选择合适的下标重载使用.下标没有限制单个纬度,你可以使用多个输入形参来定义下标以满足自定义类型的需求.
 3. 下标语法: 下标脚本允许你通过在实例名后面的方括号内写一个或多个值对该类的实例进行查询.他的语法类似于实例方法和计算属性. 使用subscript来定义下标,并且指定一个或多个输入形式参数和返回类型,与实例方法一样. 与实例方法不同的是,下标可以是读写的也可以是只读的
 4. 下标参数: 下标可以接收任意数量的输入形式参数,并且这些输入形式参数可以是任意类型. 下标也可以返回任意类型. 下标可以使用变量形式参数和可变形式参数,但是不能使用输入输出形式参数或者提供默认形式参数值.
 5. 类型下标: 实例下标就是在对应类型的实例上调用下标. 同样也可以定义类型本身的下标,这类下标叫做类型下标. 可以通过在 subscript 关键字前加上 static 关键字来标记类型下标. 在类里则使用 class 关键字允许子类重写父类的下标实现
 */

struct TimesTable {
    let multipier: Int
    subscript(index: Int) -> Int {    //使用 subscript来定义下标
        return multipier * index
    }
}
let threeTimesTable = TimesTable(multipier: 3)
print("six times three is \(threeTimesTable[6])")  //下标语法:在实例名后面的方括号写一个值


//matrix: 矩阵
struct Matrix {
    let rows: Int
    let columns: Int
    var grid: [Double]
    //初始化属性,给属性赋初始值
    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        self.grid = Array(repeating: 2.0, count: rows * columns)
    }
    //设置下标
    subscript(row: Int, column: Int) -> Double {   //实例下标:在对应类型的实例上调用下标
        //get方法获取值(类似于计算属性)
        get {
            return grid[row * columns + column]
        }
        set {
            grid[row * columns + column] = newValue   //使用默认值newValue来设置新的坐标值
        }
    }
}
var matrix = Matrix(rows: 2, columns: 2)
matrix[1, 0] = 9.9
matrix[0, 1] = 8.6
print(matrix.grid)
print(matrix.rows)
print(matrix.columns)


//enum Planet: Int {
//    case mercury = 1, venes, earth, mars, jupiter, saturn, uranus, neptune
//    static subscript(n: Int) -> Planet {   //xcode10是不能使用static关键字在这里的,xcode11是可以的
//        return Planet(rawValue: n)!
//    }
//}
//let mars = Planet[4]
//print(mars)

